package com.ht.dsp;

import _61970.core.IdentifiedObject;
import com.ht.aems.util.SerializableUtil;
import devmodel.MapObject;
import org.apache.log4j.Logger;
import org.sailing.cim.decoder.CimHandler;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/**
 * Created by IntelliJ IDEA.
 * User: Dong Shufeng
 * Date: 11-8-17
 */
public class CimToStringMap extends DefaultHandler {
    private static Logger log = Logger.getLogger(CimToStringMap.class);
    private static final String IGNORE_TYPE = "UnkownType";
    private static final String NORMAL_TYPE = "NormalType";

    private Stack<Object> types = new Stack<Object>();
    private Map<String, StringBuilder> result;
    private String currentId;
    private String currentLocalName;
    private Map<String, IdentifiedObject> neededResource;
    private StringBuilder text = new StringBuilder();

    public CimToStringMap(int size, Map<String, IdentifiedObject> neededResource) {
        result = new HashMap<String, StringBuilder>(size);
        this.neededResource = neededResource;
        CimHandler.getIgnoredPrefix().remove("cimNARI");
    }

    public void parse(String file) {
        try {
            SAXParserFactory sf = SAXParserFactory.newInstance();
            SAXParser sp = sf.newSAXParser();
            sp.parse(new InputSource(file), this);
        } catch (Exception e) {
            e.printStackTrace();
            log.warn("Exception occur when parsing file" + file + " failed to trans cim to a string map: " + e);
        }
    }

    public void startElement(String uri, String localName, String name, Attributes attribs) {
        text.setLength(0);
        if (!types.empty() && types.peek() == IGNORE_TYPE) {//check father node's type first
            types.push(IGNORE_TYPE);
            return;
        }
        // example: replace <cimNARI:EndDevice rdf:ID="280000003"> with <cim:ConductingEquipment rdf:ID="280000003">
        if (CimHandler.getSpecifiedNames().containsKey(name))
            name = CimHandler.getSpecifiedNames().get(name);
        int index = name.indexOf(":");
        String prefix = name.substring(0, index);
        localName = name.substring(index + 1);
        //
        if (CimHandler.getSpecifiedProperties().containsKey(localName))
            localName = CimHandler.getSpecifiedProperties().get(localName);
        //
        if (CimHandler.getIgnoredProperties().contains(localName)) {
            types.push(IGNORE_TYPE);
            return;
        }
        // Example: <cimNARI:SubControlArea.areaNo>1</cimNARI:SubControlArea.areaNo> is ignored
        if (CimHandler.getIgnoredPrefix().contains(prefix)) {
            types.push(IGNORE_TYPE);
            return;
        }

        // the case is for:
        // <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cim="http://iec.ch/TC57/2006/CIM-schema-cim10#">
        if (name.equals(CimHandler.RDF_RDF))
            types.push(CimHandler.CIM_EXTENSION_TYPE);
        else {
            // the case for property, such as:
            // <cim:SynchronousMachine.maximumMVAr>999.0</cim:SynchronousMachine.maximumMVAr>
            // name =  cim:SynchronousMachine.maximumMVAr; localName = SynchronousMachine.maximumMVAr; prefix = cim
            if (localName.contains(".")) {
                index = localName.lastIndexOf('.');
                //
                name = localName.substring(index + 1);
                localName = name;
                if (CimHandler.getSpecifiedProperties().containsKey(localName))
                    localName = CimHandler.getSpecifiedProperties().get(localName);
            }
            // the case for element
            // <cim:SynchronousMachine rdf:ID="SMEPC1Generator10">
            // name =  cim:SynchronousMachine; localName =  SynchronousMachine; prefix = cim
            else {
                if (CimHandler.getSpecifiedProperties().containsKey(localName))
                    localName = CimHandler.getSpecifiedProperties().get(localName);
            }

            // whether the property is ignorable
            if (CimHandler.getIgnoredProperties().contains(localName)) {
                types.push(IGNORE_TYPE);
            } else {
                currentLocalName = localName;
                for (int i = 0, size = attribs.getLength(); i < size; ++i) {
                    String attrib = attribs.getQName(i);
                    String value = attribs.getValue(i);
                    if (attrib.equals(CimHandler.RDF_ID)) {
                        currentId = attribs.getValue(i).trim();
                        if (!neededResource.containsKey(currentId)) {
                            types.push(IGNORE_TYPE);
                            return;
                        }
                        result.put(currentId, new StringBuilder());
                        result.get(currentId).append(MapObject.KEY_ID).append(SerializableUtil.PROP_SEPARATOR).append(value).append(SerializableUtil.ENTITY_SEPARATOR);
                        result.get(currentId).append(MapObject.KEY_TYPE).append(SerializableUtil.PROP_SEPARATOR).append(localName).append(SerializableUtil.ENTITY_SEPARATOR);
                        result.get(currentId).append(MapObject.KEY_CLASS).append(SerializableUtil.PROP_SEPARATOR).append(localName).append(SerializableUtil.ENTITY_SEPARATOR);
                    } else if (attrib.equals(CimHandler.RDF_RESOURCE)) {
                        //<cim:PowerTransformer.transformerType rdf:resource="http://iec.ch/TC57/2006/CIM-schema-cim10#TransformerType.voltageControl"/>
                        //should be
                        //<cim:PowerTransformer.transformerType> voltageControl</cim:PowerTransformer.transformerType>
                        //<p/>
                        if (currentId == null)
                            return;
                        String typeText;
                        if (value.startsWith("http://")) {
                            index = value.lastIndexOf(".");
                            typeText = value.substring(index + 1);
                        } else {
                            if (value.startsWith("#"))
                                typeText = value.substring(1);
                            else
                                typeText = value;
                        }
                        text.append(typeText);
                    }
                }
                types.push(NORMAL_TYPE);
            }
        }
    }

    public void endElement(String uri, String localName, String qName) {
        if (types.pop() != NORMAL_TYPE || currentLocalName == null)
            return;
        result.get(currentId).append(currentLocalName).append(SerializableUtil.PROP_SEPARATOR).append(text).append(SerializableUtil.ENTITY_SEPARATOR);
        text.setLength(0);
        currentLocalName = null;
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        if (types.peek() != NORMAL_TYPE || currentLocalName == null)
            return;
        text.append(ch, start, length);
    }

    public Map<String, StringBuilder> getResult() {
        return result;
    }
}
